Einfhrung NAUDIO Die Samplelibrary Mulle KybernetiK Version 1.0 Copyright auf Dokumentation and Programmcode ½ 1993 Mulle KybernetiK "Bang that bit that doesn't bang" Copyright 1993 Mulle KybernetiK Alle Rechte Vorbehalten. Das Werk einschliežlich seiner Teile ist urheberrechtlich geschtzt. Das vorliegende Werk darf, auch auszugsweise, ohne schriftliche Genehmigung von Mulle KybernetiK weder reproduziert, bertragen umgeschrieben auf Da- tentr„gern gespeichert oder in eine andere Sprache bersetzt werden, weder in mechanischer, elektronischer, positronischer, magnetischer, optischer, chemi- scher oder biologischer Form. Geschrieben, gesetzt und gedruckt mit Tempus-WORD auf ATARI. 1. Auflage Mulle KybernetiK 1993, Bochum Printed in Germany Mulle KybernetiK c/o Wallmann Buchenweg 2 5810 Witten-Buchholz Germany 1-Der schnelle Weg zum Erfolg Die nun folgenden "Wege zum Erfolg" reizen die M”glichkeiten der NAU- DIO-Bcherei bei weitem nicht aus. Wenn sie also ber die hier vorgestell- te Funktionalit„t hinausgehen wollen, sollten sie das "Technische Manual" und das "Reference Manual" studieren. 1.1 Am Anfang war das Programm und der Compiler sah, daž es gut war. Das folgende kleine Beispielprogramm demonstriert in ca. 50 Zeilen be- reits die wichtigsten Funktionen der NAUDIO-Bibliothek. Es mag durch- aus der Fall sein, daž sie nie weiter als dieses Kapitel lesen brauchen, wenn sie einfach nur auf relativ portable Art und Weise Samples auf allen m”glichen Atari-Computern(1) abspielen wollen. Zu Beginn einer C-Datei, die NAUDIO-Bibliotheken verwendet, sollte im- mer die Anweisung #include stehen. Damit werden s„mtliche fr NAUDIO ben”tigten Includedateien geladen. Da wir fprintf fr Demonstrationszwecke verwenden wollen, binden wir in diesem Bei- spiel (Reihenfolge brigens unwichtig) auch noch die C-Standardbibli- othek ein. Die Definition der Funktion error bergehen wir einstweilen und wenden uns gleich dem Hauptteil main zu: #include #include void error( s) char *s; { fprintf( stderr, "demo: Error \"%s\"\n", s); naudio_done(); exit( 1); } main() { n_sample *p; naudio_init(); naudio_all(); if( naudio_engine( SAMPLES) >= 0) { if( p = nsample_load( "zarths.smp")) { channel *q; if( ! naudio_start( 0)) { if( q = nsample_play( p, 0L, NAUDIO_MAX_VOL, 1)) { while( q->play);/* warten auf Godot */ naudio_stop(); channel_free( q); } else error( "Kein Kanal mehr vorhanden"); naudio_stop(); } else error( "Versagen des Starters"); nsample_free( p); } else error( "Sample nicht geladen"); naudio_done(); } else error( "Engine nicht initialisiert"); return( 0); } Zufrdererst sollte jedes NAUDIO-Programm die Routine naudio_init aufrufen. Diese setzt einige wichtige interne Werte, die fr einen reibungslosen Ablauf von N”ten sind, nachdem sie die verfgbare Hardware analysiert hat. Danach sollten sie sich entscheiden ob sie alle Enginetypen einbinden wollen (naudio_all) oder nur die wichtigsten (naudio_some). Je weniger sie einbinden, desto kleiner wird auch ihre Programm, und in den meisten F„llen ist naudio_some v”llig ausreichend. if( naudio_engine( SAMPLES) >= 0) Danach wird das Audiosystem fr das Abspielen von Samples durch naudio_engine vorbereitet. Der Rckgabewert ist bei Erfolg positiv oder 0, so daž wir jetzt beruhigt ein Sample aus einer Datei laden k”nnen. Bis jetzt findet noch keine Sounderzeugung statt! if( q = nsample_play( p, 0L, NAUDIO_MAX_VOL, 1)) Zum Laden eines Samples aus einer Datei verwendet man die Funktion nsample_load, die im Erfolgsfall einen Zeiger auf ein n_sample zurckgibt. Ein n_sample ist eine Struktur die Informationen ber das Sample wie dessen L„nge und dessen Samplewerte beinhaltet. (In main haben wir als erstes ein Zeiger auf ein solches n_sample definiert). Nehmen wir an, daž das Sample tats„chlich geladen wurde (der Wert von p also nicht NULL ist), so wird als n„chstes mit naudio_start das Sound- system angeworfen. Ab jetzt werden also Samplewerte an die Hardware ausgegeben. Da wir aber kein Sample zum Abspielen designiert haben, drfte vorl„ufig immer noch nichts zu h”ren sein. if( q = nsample_play( p, 0L, NAUDIO_MAX_VOL, 1)) Mit nsample_play wird ein Kanal des Soundsystems reserviert und ein Sample auf ihm gespielt. Der Rckgabewert der Funktion ist ein Zeiger auf den reservierten Kanal. Auf einem solchen Kanal kann brigens zu ei- nem Zeitpunkt immer nur ein monophones Sample gespielt werden. Das erste Argument der Funktion ist die Adresse des geladenen Sam- ples, welches wir spielen m”chten (Im Beispiel: der Wert von p). Mit dem zweiten Parameter, den wir auf 0 gesetzt haben, legen wir fest, daž das Sample mit der Frequenz gespielt werden soll, mit der es aufgenommen wurde(2). Der dritte Parameter setzt die Lautst„rke auf Maximum. Der vierte Parameter bedeutet nsample_play, daž mit dem Abspielen des Sample sofort begonnen werden soll. šber den ->play Eintrag des Kanals warten wir darauf, daž das Sample fertig ist. Natrlich k”nnte man hier auch noch etwas ntzlicheres ma- chen, da das Sample, egal auf welcher Hardware, immer im Hintergrund gespielt wird. Solange ->play nicht 0 ist, wird das Sample noch gespielt.(3) ACHTUNG: Wichtig ist die Unterscheidung zwischen geladenen Samples (n_sample) und den Kan„len des Soundsystems (channel). Die jeweili- gen Strukturen sind unterschiedlich und sollten nicht miteinander verwechselt werden. Aus didaktischen Grnden schalten wir das Soundsystem mit nau- dio_stop nach Beendigung des Samples sofort wieder aus. Dies spart im brigen auch in jedem Fall, auch auf dem STE oder Falcon, Prozessorzeit. Anschliežend geben wir den Kanal mit channel_free wieder frei. M”chte man in eigenen Programmen mehrere Samples nacheinander spielen, so ist dies empfehlenswert(4), da naudio_play immer versucht einen neuen Kanal zu reservieren. Hat man aber nie einen Kanal zurckgegeben, ist ir- gendwann mal Schluž, da die Zahl der Kan„le auf acht beschr„nkt ist. Da wir mit dem Soundsystem nichts mehr vorhaben, k”nnen wir naudio_done aufrufen. Dieser Aufruf muž, nachdem naudio_engine(5) einmal aufgerufen wurde, vor Beendigung des Programms gemacht werden. Nichtbeachtung dieser Regel wird blicherweise mit schweren Crashs nicht unter zwei Bomben bestraft. Wie man aus der Definition und Benutzung der error-Funktion im Bei- spielprogramm ersehen kann, ist ein berflssiger Aufruf von nau- dio_done jedoch v”llig unsch„dlich. Auch geladene Samples sollten sobald wie m”glich der Wiederverwer- tung zugefhrt werden, da Samples im allgemeinen speicherfressende Dinger sind. Eines mit nsample_load geladenes Samples entledigt man sich mittels nsample_free. 1.1.1 AUS! Einen Kanal kann man auch vorzeitig mit channel_stop auschalten. Ein Ausschalten mit gleichzeitigem Freigeben erfolgt via channel_delete. Mit diesem Wissen k”nnen wir nun eine kleine C-Funktion schreiben,. die ein Sample aus einer Datei liest, dieses spielt und zurckkehrt, wenn entwe- der eine Taste auf dem Keyboard gedrct wurde oder das Sample ver- klungen ist: void load_n_play( char *filename) { n_sample *p; channel *q; if( p = nsample_load( filename))/* laden, != 0 bei Erfolg */ { if( q = nsample_play( p, 0L, NAUDIO_MAX_VOL, 1))/* spielen*/ { while( q->play)/* so lange der channel aktiv ist */ { if( Bconstat( 2))/* Taste gedrueckt ? */ { Bconin(2);/* Taste einlesen & ignorieren */ channel_stop( q);/* ja, dann Kanal aus */ break; } } channel_free( q);/* Kanal freigeben */ } nsample_free( p);/* Sample entsorgen*/ } } 1.2 Encore mit einem Modul Das Abspielen von Modulen mit NAUDIO gestaltet sich auch nicht diffizi- ler als das Abspielen von Samples. Sie glauben es nicht ? Ein weiteres Beispielprogramm wird sie eines Besseren belehren! #include #include void error( s) char *s; { fprintf( stderr, "demo: Error \"%s\"\n", s); naudio_done(); exit( 1); } main() { n_module *p; naudio_init(); naudio_some(); if( naudio_engine( NTRACKER) >= 0) { if( p = nmodule_load( "beat_dis.mod")) { if( ! naudio_start( 0)) { nmodule_play( p, 0L, 0, 0, 0); while( ntrack->song); naudio_stop(); } else error( "Versagen des Starters"); nmodule_free( p); } else error( "Modul nicht geladen"); naudio_done(); } else error( "Engine nicht initialisiert"); return( 0); } Der Anfang ist wie gehabt. Anstelle eines Zeigers auf eine Sample ben”ti- gen wir jedoch nun einen Zeiger auf ein Modul. Klarer Fall ein n_module beinhaltet die Modulinformation. šbrigens sind die in einem n_module enthaltenen Samples wiederum n_samples! Geladen werden Module per nmodule_load, welches das bliche Pro- Tracker-Format (und bald auch das NAUDIO-spezifische NaTracker-For- mat) kennt. Das Resultat der Ladeoperation ist ein Zeiger auf ein n_module oder aber ein NULL-Zeiger, wenn etwas schiefgegangen ist. Das Soundsystem wird diesmal fr den NTRACKER(6) vorbereitet, also bekommt dies naudio_engine anstatt SAMPLES als Parameter bergeben. Ein Modul wird mit nmodule_play gestartet. Die letzten vier Parameter sollten fr den Anfang immer 0 gesetzt sein. Mit dem ersten Parameter bestimmen wir welches n_module gespielt werden soll, welches in diesem Fall, oh Wunder, das mit nmodule_load geladene ist. Da ein Modul mehrere Kan„le des Soundsystems verwendet, k”nnen wir nicht auf die Beendigung eines Kanals warten. Darberhinaus k”nnen in einem Modul auch Pausen auftreten, in denen kein Kanal ein Sample spielt. Also ist fr Module das ->play Feld eines channel nicht fr unsere Zwecke, das Warten auf das Ende des Moduls, brauchbar. In der globalen Struktur ntrack(7) befinden sich Eintr„ge, die den Zustand des Trackers beschreiben. Hier existiert auch ein Eintrag ntrack->song der ein Zeiger auf das gespielte Modul ist. Wenn dieser Wert auf NULL wechselt, ist der Tracker mit dem Modul fertig. Also prft das Beispielprogramm in einer Schleife, bis dies passiert. Ein Modul kann man aber auch mit nmodule_stop abbrechen, bevor das Ende erreicht worden ist. Analog zu nsample_free existiert die Funktion nmodule_free die ein Modul nach Gebrauch wieder aus dem Speicher l”scht. 1.3 Das Gerst Das Gerst einer NAUDIO-Applikation besteht also aus folgenden Ele- menten. o Einbindung der Includedatei. o Am Anfang der Aufruf von naudio_init, welches die Computerkonfiguration analysiert und mit den gewonnenen Erkenntnissen einige interne Variablen initialisiert. o Danach werden die Enginetypen, die benutzt werden sollen, mit naudio_all oder naudio_some eingebunden. o Das Soundsystem wird mit naudio_engine initialisiert. Zu diesem Zeitpunkt steht fest, welches Sampleformat und welches Ausgabeger„t benutzt werden soll. o Jetzt k”nnen Module und Samples geladen und gespielt werden. Das Soundsystem wird mit naudio_start eingeschaltet. o Das Soundsystem wird mit naudio_stop ausgeschaltet. o Mit naudio_done wird die NAUDIO-Applikation abgeschlossen, bevor das Programm terminiert. 1.3.1 Zwischenspiel Mit den bisher vorgestellten Funktionen lassen sich hardwareunabh„ngi- ge Programme fr den Atari ST(E),TT und Falcon entwickeln. Die Wahl der benutzen Audiohardware ist fr das Programm v”llig transparent. Dies zieht natrlich den Nachteil mit sich, daž solche portablen Program- me, auch einen gr”žeren Umfang haben als solche, die nur fr eine be- stimmte Hardwarekonfiguration geschrieben wurden. Aber wir k”nnen auch anders! ACHTUNG: Aus Grnden der šbersichtlichkeit machen die folgenden Beispiele nur rudiment„re Fehlerabfragen. Nicht unbedingt zur Nachahmung empfohlen. 1.4 NAUDIO explizit #include main( argc, argv)/* als .TTP compile oder danach umbenennen */ int argc; char *argv[]; { n_sample *p; if( argc != 2) return( 1); naudio_init(); engine_psg(); nsample_engine( 12500L, PSG_ENGINE);/* NAUDIO-Samples*/ if( p = nsample_load( argv[ 1]))/* Sample laden */ { channel *q; naudio_start( 0);/* NAUDIO starten */ q = nsample_play( p, 0L, NAUDIO_MAX_VOL, 1); while( q->play);/* Auf das Ende des Samples warten */ channel_free( q);/* NAUDIO sample zurckgeben */ nsample_free( p);/* geladenes Sample zurckgeben */ naudio_stop(); } naudio_done();/* NAUDIO beenden */ return( 0); } Der Anfang ist wie gehabt, zun„chst laden wir ein Sample und merken uns dessen Adresse in der Zeigervariablen p. Dann jedoch kommt etwas neues. Statt des Aufrufs von naudio_engine stehen da zwei unbekannte Funktionen naudio_psg und nsample_engine. Da wir auf den Komfort der naudio_engine Funktion verzichten wollen, mssen wir selbst die Ausgabeger„tschaft und deren Frequenz bestimmen, nachdem wir wie zuvor NAUDIO mit naudio_init initialisiert haben. Mit naudio_psg installiert man die ben”tigten Funktionen um Samples ber den PSG-Chip(8) abzuspielen. HINWEIS: Dadurch, daž die Funktionen der PSG_ENGINE erst zur Laufzeit instal- liert werden, erreicht man, daž alle anderen Funktionen, die die Klangerzeugung fr die Falcon- und den STE/TT-Audiohardware ab- wickeln, nicht zu dem Programm dazugebunden werden. Benutzt man naudio_some so bekommt man einen kompletten Satz Routinen fr FALCON, STE, STARSAMPLER und PSG. Anstelle des SAMPLES-Parameter der naudio_engine Routine, rufen wir die Funktion direkt auf, die NAUDIO fr das Abspielen von Samples vor- bereitet. Als ersten Parameter gibt man die Frequenz an mit der NAUDIO das Soundsystem betreiben soll. Diese Frequenz hat nicht viel mit der Frequenz zu tun, mit der man die Samples abspielen m”chte, sondern haupts„chlich mit der Qualit„t, mit der die Samples reproduziert werden. Im allgemeinen gilt, je h”her die Frequenz des Soundsystems, desto bes- ser die Ausgabequalit„t. Allerdings bedeutet eine hohe Frequenz auch, daž die CPU mehr belastet wird. Die Frequenzangabe ist aber nur ein Wunschwert, dem NAUDIO versucht soweit wie m”glich entgegen zu kommen. Die tats„chliche realisierte En- ginefrequenz wird von nsample_engine zurckgegeben. Wir initialisieren NAUDIO fr das Abspielen von Samples mit einer Fre- quenz von ca. 12500 Hz und w„hlen als Ausgabeger„t den PSG Sound- chip(9). (Eine Frequenz von 12500 Hz auf der PSG_ENGINE ist auf allen Atari Modellen machbar). Also wird dieses Beispielprogramm auch auf einem Falcon das Sample ber den PSG-Chip abspielen. Um Module anstelle von Samples zu spielen, ersetzt man den Aufruf von nsample_engine durch ntrack_engine, wie folgt: ntrack_engine( 11000L, PSG_ENGINE, PAL); Hierbei bestimmt 11000L wieder die Frequenz fr das Soundsystem und der zweite Parameter definiert das Ausgabeger„t. Was der letzte Parame- ter bedeutet erkl„rt Ihnen das "Technische Handbuch". 1.5 Zweikanalton Unter Einsatz modernster Technik ist es Mulle KybernetiK gelungen Zwei- kanalton, wie es ARD & ZDF vorgemacht haben, auch auf Ihren Atari zu bringen. Oh Freude! Die bisherigen Beispielprogramme, die Samples spielten, haben sich le- diglich eines Kanals des Soundsystems bedient. NAUDIO kann aber in je- dem Fall vier Kan„le gleichzeitig spielen, unter Umst„nden sogar acht. Wie macht man das ? Hilfe verspricht die Funktion naudio_set_channels, die die Anzahl der benutzten Kan„le festsetzt. Hat man NAUDIO fr SAMPLES mit nau- dio_engine oder mit nsample_engine vorbereitet, so ist die Einstellung der maximal benutzbaren Kan„le auf eins begrenzt. Es steht Ihnen jedoch jederzeit frei diese mit naudio_set_channels zu erh”hen. Die Werte von 1-4 werden dabei fr jeden Ausgabetyp garantiert. Darber hinausgehen- de Werte k”nnen u.U. zu Fehlern fhren. ACHTUNG: Der Wert den man naudio_set_channels mitgibt, wird von NAUDIO entweder bernommen, oder durch den n„chst h”heren verfgbaren ersetzt. Kann eine Konfiguration nicht drei aber vier Stimmen gleich- zeitig spielen, so w„hlt NAUDIO vier. #include main() { n_sample *p; channel *q[ 2]; naudio_init(); naudio_some(); naudio_engine( SAMPLES); if( p = nsample_load( "hello_w.nsx")) { naudio_set_channels( 2); naudio_start( 0); q[ 0] = nsample_play( p, 0L, NAUDIO_MAX_VOL, 1); q[ 1] = nsample_play( p, p->frq + 1000, NAUDIO_MAX_VOL, 1); while( q[ 0]->play); naudio_stop(); channel_free( q[ 0]); channel_free( q[ 1]); nsample_free( p); } naudio_done(); } Das Setzen von naudio_set_channels bei "laufendem Motor" und einem aktiven Sample kann zu einem kurzen Aussetzer fhren. Eine Besonderheit des Beispiels sollte noch schnell Erw„hnung finden. Ein n_sample enth„lt einen Eintrag, daž die Aufnahmefrequenz des Sam- ples enth„lt. Dieser Eintrag heižt ->frq. Im Beispielprogramm wird diese Information benutzt um das Sample zweistimmig erklingen zu lassen, in dem man das zweite Sample um 1000Hz transponiert spielt. 1.6 Motorwechsel Es ist prinzipiell m”glich die laufende Konfiguration zu wechseln, in dem man zun„chst die vorhandene Konfiguration mit naudio_done zerst”rt, und darauf eine neue mit naudio_engine baut. Diese Vorgehensweise hat allerdings ein prinzipielles Problem, daž sich unter Umst„nden die in- terne Repr„sentation der Samples „ndert(10) (siehe T.H.). Dies ist nur dann unproblematisch(11), solange man ausschliežlich die Routinen nsample_play oder nmodule_play zum Abspielen benutzt, da diese ev. eine (destruktive) Repr„sentationskonversion vornehmen. Fr die meisten Anwenderprogramme sollte es aber ausreichend sein, entweder fix eine Vorgabe zu treffen und diese nicht zu ver„ndern, oder beim Start die En- gineart festzulegen, die benutzt werden soll. Im Hinblick auf zuknftige 16-Bit Applikationen erscheint es darberhi- naus mehr als fraglich, ob eine Konversion von 16-Bit zu 8-Bit und dann wieder zurck auf 16-Bit ohne Qualit„tsverlust (und gr”žere Umst„nde) noch machbar bzw. wnschenswert ist. Das Fazit fr den Anwendungsprogrammierer, die dynamische Ver„nde- rung der NAUDIO-Konfiguration ist mit Vorsicht zu geniežen, es wird nicht gerade davon abgeraten, aber es wird auch nicht unbedingt dazu ermu- tigt.